#include "graphic_public.h"
#include "chipmunk_object.h"
#include <vector>

//extern cpSpace *space;
#pragma warning(disable : 4244)

namespace JointsManager {
	std::list<delegator_t> g_jointsRenderDelegators;

	void clearJoints() {
		g_jointsRenderDelegators.clear();
	}

	void RenderJoints()
	{
		BOOST_FOREACH(delegator_t &tmp, g_jointsRenderDelegators) {
			tmp._render();
		}
	}

	bool Intersect( const point2d_t a, const point2d_t b )
	{
		bool ret = false;
		for (std::list<delegator_t>::iterator iter = g_jointsRenderDelegators.begin(); 
			iter != g_jointsRenderDelegators.end();) {
				if (iter->_intersect(a, b)) {
					iter->_free();
					iter = g_jointsRenderDelegators.erase(iter);
					ret = true;
				}
				else {
					iter++;
				}
		}
		return ret;
	}
	void updateJoints(cpBody *original, 
		cpBody *clip_part0, const point2d_t *vts0, int num0, point2d_t gravity_center0, 
		cpBody *clip_part1, /*const point2d_t *vts1, int num1,*/ point2d_t gravity_center1) {
			BOOST_FOREACH(delegator_t &tmp, g_jointsRenderDelegators) {
				tmp._updateJoint(original, 
					clip_part0, vts0, num0, gravity_center0, clip_part1, gravity_center1);
			}
	}

	void draw_line(cpVect a, cpVect b) 
	{

		glEnable(GL_TEXTURE_2D);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glColor4f(1.0, 1.0, 1.0, 1.0);
		
#ifndef WIN32
		static ExTexture2D texNail((NSString*)@"nail.png");
		static ExTexture2D texRope((NSString *)@"rope.png2");
		const float_t rope_height = 4;
		const float_t rope_width = 512;
		const float_t half_height = rope_height / 2;
		cpVect vect = (cpvsub(b, a));
		cpFloat lenght = cpvlength(vect);
		vect = cpvnormalize(vect);
		cpVect perp_vect = cpvperp(vect);
		perp_vect = cpvmult(perp_vect, half_height);

		glBindTexture(GL_TEXTURE_2D, [texRope.GetTexture() name]);
		float_t ratio = lenght / rope_width;
		ASSERT(ratio <= 1);
		ASSERT(rope_width == [texRope.GetTexture() contentSize].width);

		const GLfloat	 coordinates[] = {
			0,		[texRope.GetTexture() maxT],
			0,		0,
			[texRope.GetTexture() maxS] * ratio,	0,			
			[texRope.GetTexture() maxS] * ratio,	[texRope.GetTexture() maxT]
		};


		cpVect pt[4];
		pt[0] = cpvsub(a, perp_vect);
		pt[1] = cpvadd(a, perp_vect);
		pt[2] = cpvadd(b, perp_vect);
		pt[3] = cpvsub(b, perp_vect);		

		GLfloat	vertices[] = {
			pt[0].x, pt[0].y,
			pt[1].x, pt[1].y,
			pt[2].x, pt[2].y,
			pt[3].x, pt[3].y,
		};


		glVertexPointer(2, GL_FLOAT, 0, vertices);
		glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
		glDrawArrays(GL_TRIANGLE_FAN, 0, 4);			

		[texNail.GetTexture() drawAtPoint:CGPointMake(a.x - [texNail.GetTexture() contentSize].width/2, a.y - [texNail.GetTexture() contentSize].height/2)];
		[texNail.GetTexture() drawAtPoint:CGPointMake(b.x - [texNail.GetTexture() contentSize].width/2, b.y - [texNail.GetTexture() contentSize].height/2)];
#endif
		glDisable(GL_TEXTURE_2D);
	}
};

bool ObjectManager::Intersect( const point2d_t a, const point2d_t b )
{
	bool ret = false;
	obj_delegate_t d0, d1;
	std::vector<obj_delegate_t> newObj;
	newObj.reserve(0x20);

	for (std::list<obj_delegate_t>::iterator iter = m_resource.begin(); 
		iter != m_resource.end();) {
			if (iter->_intersect(m_space, a, b, d0, d1)) {
				iter->_remove_from_space(m_space);
				iter->_free();
				iter = m_resource.erase(iter);
				newObj.push_back(d0);
				newObj.push_back(d1);
				ret = true;
			}
			else {
				iter++;
			}
	}
	BOOST_FOREACH(obj_delegate_t &tmp, newObj) {
		m_resource.push_back(tmp);
	}
	return ret;
}

void SpaceManger::initialize()
{
	//todo, relase;
	cpInitChipmunk();
	ASSERT(NULL == m_space);
	// Optional. Read the docs to see what this really does.
	cpResetShapeIdCounter();
	// Create a space and adjust some of it's parameters.
	m_space = cpSpaceNew();
	cpSpaceResizeStaticHash(m_space, 20.0, 999);

	//set gravity
	m_space->gravity = cpv(0, -950);
	m_space->damping = 0.78;

	m_ObjManager.reset(new ObjectManager(m_space));
	JointsManager::clearJoints();
	
	glEnable(GL_LINE_SMOOTH);
	glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
}

void SpaceManger::release()
{
	if (m_space) {
		cpSpaceFreeChildren(m_space);
		cpSpaceFree(m_space);
		m_space = NULL;
	}
	m_ObjManager.reset(NULL);
	JointsManager::clearJoints();
}

#ifdef WIN32
//extern void pickingFunc(cpShape *shape, void *data);
//extern void drawCollisions(void *ptr, void *data);
//extern void draw_object_wrap(void *ptr, void *unused);
extern cpVect mousePoint;
#else

#endif // WIN32
void draw_object_wrap(void *ptr, void *unused)
{
	cpShape *shape = (cpShape *)ptr;
	if (NULL == shape->data) {
		ASSERT(0); return ;
	}
	action_t *p = (action_t *)shape->data;
	p->render_action();
}


void SpaceManger::display()
{
	ASSERT(m_space);
	
#ifdef WIN32
	glClear(GL_COLOR_BUFFER_BIT);

	//	glColor4f(0.6, 1.0, 0.6, 1.0);
	//	cpSpaceHashEach(space->activeShapes, &drawBB, NULL);
	//	cpSpaceHashEach(space->staticShapes, &drawBB, NULL);

	glColor4f(1.0, 0.0, 0.0, 1.0);
	cpSpaceHashEach(m_space->activeShapes, &draw_object_wrap, NULL);
	cpSpaceHashEach(m_space->staticShapes, &draw_object_wrap, NULL);

	glColor4f(1.0, 0.0, 0.0, 1.0);
	//cpSpaceShapePointQuery(m_space, mousePoint, pickingFunc, NULL);
	//cpSpaceStaticShapePointQuery(m_space, mousePoint, pickingFunc, NULL);

	cpArray *bodies = m_space->bodies;
	int num = bodies->num;

	glBegin(GL_POINTS); {
		glColor3f(0.0, 0.0, 1.0);
		for(int i=0; i<num; i++){
			cpBody *body = (cpBody *)bodies->arr[i];
			glVertex2f(body->p.x, body->p.y);
		}

		glColor3f(1.0, 0.0, 0.0);
		//cpArrayEach(m_space->arbiters, &drawCollisions, NULL);
	} glEnd();	
	
#else
	cpSpaceHashEach(m_space->activeShapes, &draw_object_wrap, NULL);
	cpSpaceHashEach(m_space->staticShapes, &draw_object_wrap, NULL);
#endif // WIN32

	JointsManager::RenderJoints();
}

void SpaceManger::update( cpFloat dt )
{
	ASSERT(m_space);

	//set or reset forces here if needed
	cpSpaceStep(m_space, dt);
}